#include  <stdint.h>
#include  <string.h>
#include <stdbool.h>

#include "fatfs.h"

#include "menu.h"
#include "audio.h"

// #include "ili9488.h"

#include  "decoder.h"

#include  "flac.h"



//This is the size of the memory block that the decoders store all their work in
//It should be set to the largest value it ever needs to be (currently FLAC defined)

#define decoderScatchSize MAX_FRAMESIZE + MAX_BLOCKSIZE * 8

// FIL FLACfile;

FLACContext context;

int sampleShift;
int16_t samplePair[2];

UINT bytesLeft, bytesUsed, s1;

unsigned char *fileChunk;

int32_t *decodedSamplesLeft;
int32_t *decodedSamplesRight;

uint16_t max_framesize;
uint16_t max_blocksize;
uint16_t align;

// __align(8) uint8_t decode_Left[4 * 460];
// __align(8) uint8_t decode_Right[4 * 460];

// __attribute__ ((aligned (8))) uint8_t decode_Left[4 * 4608];
// __attribute__ ((aligned (8))) uint8_t decode_Right[4 * 4608];

// extern  uint8_t *decode_Left;
// extern  uint8_t *decode_Right;

// unsigned char flac_buffer[24 * 1024];

// Buffer for all decoders
// unsigned char wave_stream[decoderScatchSize];

// extern unsigned char* buffPointer;

int8_t x, y, z;
uint8_t *p8;
uint8_t *pbuf;

uint32_t  tickets;

// --------------------------------------------
static void frame_24_32(int16_t *data, uint32_t size)
{
	pbuf = (uint8_t *)data;

	for(int i = 0; i < size / 4; i++) {
		x = pbuf[1];
		y = pbuf[2];
		z = pbuf[0];

		pbuf[0] = x;
		pbuf[1] = y;
		pbuf[2] = 0;
		pbuf[3] = z;
		pbuf += 4;
	}
}


static void set_volume(int bps, int size, int16_t *data)
{
	uint16_t  num_vol;
	int16_t   *temp_vol;
	// int32_t   *temp_vol_24;
	int32_t   vol;

	temp_vol = data;

	if(bps == 16) {

		for(num_vol = 0; num_vol < size; num_vol++)
			*temp_vol++ >>= volume;

	} else if(bps == 24) {

		for(num_vol = 0; num_vol < size; num_vol++) {
			vol = temp_vol[num_vol * 2 + 1] | ((temp_vol[num_vol * 2]) << 16);

			vol >>= volume;

			temp_vol[num_vol * 2] = (int16_t)(vol & 0xffff);
			temp_vol[num_vol * 2 + 1] = (int16_t)((vol >> 16) & 0xffff);
			// *temp_vol_24++ >>= (volume-1);
		}
	}
}

//converts string to all uppercase letters
static void strToUppercase(char *string)
{
	unsigned long i = 0;

	while(string[i] != '\0') {
		if(string[i] >= 97 && string[i] <= 122) {
			string[i] -= 32;
		}
		i++;
	}
}

// __attribute__ ((section ("RAMCODE")))
void process_flac_buffer(void)
{
	Is_Process_buffer = false;

	// printf("process_flac_buffer\r\n");
	if(Is_finished) {

		audio_Stop();

		printf("\r\n --- Is_finished --- \r\n");

		f_close(&SDFile);

		Play_Next();
		return ;
	}

	pAudio->exchange	= pAudio->play_buff;
	pAudio->play_buff	= pAudio->read_buff;
	pAudio->read_buff	= pAudio->exchange;

	if (bytesLeft) {

		if( pAudio->bitsPerSamplePerChannel == 16) {
			if(decode_frame_16(&context, fileChunk, bytesLeft, (int16_t *)pAudio->read_buff) < 0) {
				printf("FLAC Decode Failed\r\n");
				return;
			} else {
				printf("\rDecode -> %6d <> %6d", context.blocksize, bytesLeft);
				pAudio->bytesRead = context.blocksize * 4;
			}
			if(volume)	set_volume(16, pAudio->bytesRead / 2, (int16_t *)pAudio->read_buff);

		} else if( pAudio->bitsPerSamplePerChannel == 24) {

			context.decoded0 = (int32_t *) & (pAudio->read_buff[0]);
			context.decoded1 = (int32_t *) & (pAudio->read_buff[max_blocksize + max_blocksize]);

			// printf("decode0 : 0x%x decode1 : 0x%x buf_A : 0x%x buf_B : 0x%x\r\n", context.decoded0, context.decoded1, pAudio->buff_A, pAudio->buff_B);

			// if(decode_frame_24_lite(&context, fileChunk, bytesLeft, (int32_t *)pAudio->read_buff) < 0) {
			if(decode_frame_24_lite(&context, fileChunk, bytesLeft) < 0) {
				printf("FLAC Decode Failed\r\n");
				return;
			} else {
				printf("\r->  blocksize : %6d <>  bytesLeft : %6d", context.blocksize, bytesLeft);
			}

			pAudio->bytesRead = context.blocksize * 8;

			pbuf = (uint8_t *)pAudio->read_buff;
			for(int i = 0; i < pAudio->bytesRead / 4; i++) {
				/***
				x = pbuf[1];
				y = pbuf[2];
				z = pbuf[0];

				pbuf[0] = x;
				pbuf[1] = y;
				pbuf[2] = 0;
				pbuf[3] = z;
				***/

				pbuf[3] = pbuf[0];
				pbuf[0] = pbuf[1];
				pbuf[1] = pbuf[2];
				pbuf[2] = 0;

				pbuf += 4;
			}
			// if(volume)	set_volume(24, pAudio->bytesRead / 4, (int16_t *)pAudio->read_buff);
		}

		//calculate the number of valid bytes left in the fileChunk buffer
		bytesUsed = context.gb.index / 8;
		bytesLeft -= bytesUsed;
		//shift the unused stuff to the front of the fileChunk buffer
		memmove(fileChunk, &fileChunk[bytesUsed], bytesLeft);
		//Refill the fileChunk buffer

		// Is_Busy = 1;

		retSD = f_read(&SDFile, &fileChunk[bytesLeft], max_framesize - bytesLeft, &s1);
		if(retSD != FR_OK) {
			printf("f_read() failed, res = %d\r\n", retSD);
			f_close(&SDFile);
			Is_finished = true;
		}
		bytesLeft += s1;
		// Is_Busy = 0;
	} else {
		audio_Stop();
		printf("\r\nplay finished  %02d:%02d\r\n", min, sec);
		f_close(&SDFile);
		// Is_finished = true;
		Play_Next();
	}
}

//This function creates the FLACContext the file at filePath
//Called by main FLAC decoder
//See http://flac.sourceforge.net/format.html for FLAC format details
//0 context is valid; 1 context is not valid
static int Flac_ParceMetadata(const TCHAR filePath[], FLACContext *context)
{
	UINT s1 = 0;
	// FIL FLACfile;
	int metaDataFlag = 1;
	char metaDataChunk[128];
	unsigned long metaDataBlockLength = 0;
	char *tagContents;

	if((retSD = f_open(&SDFile, filePath, FA_READ)) != FR_OK) {
		printf("Could not open: %s -> %d\r\n", filePath, retSD);
		return 1;
	} else {
		printf("\r\n>>>Open: %s OK\r\n", filePath);
	}

	retSD = f_read(&SDFile, metaDataChunk, 4, &s1);
	if(s1 != 4) {
		printf("Read failure -> %d\r\n", retSD);
		f_close(&SDFile);
		return 1;
	}

	if(memcmp(metaDataChunk, "fLaC", 4) != 0) {
		printf("Not a FLAC file\r\n");
		f_close(&SDFile);
		return 1;
	}

	// Now we are at the stream block
	// Each block has metadata header of 4 bytes
	do {
		f_read(&SDFile, metaDataChunk, 4, &s1);
		if(s1 != 4) {
			printf("Read failure\r\n");
			f_close(&SDFile);
			return 1;
		}

		//Check if last chunk
		if(metaDataChunk[0] & 0x80) metaDataFlag = 0;
		metaDataBlockLength = (metaDataChunk[1] << 16) | (metaDataChunk[2] << 8) | metaDataChunk[3];
		//STREAMINFO block
		if((metaDataChunk[0] & 0x7F) == 0) {
			if(metaDataBlockLength > 128) {
				printf("Metadata buffer too small\r\n");
				f_close(&SDFile);
				return 1;
			}

			f_read(&SDFile, metaDataChunk, metaDataBlockLength, &s1);
			if(s1 != metaDataBlockLength) {
				printf("Read failure\r\n");
				f_close(&SDFile);
				return 1;
			}
			/*
			<bits> Field in STEAMINFO
			<16> min block size (samples)
			<16> max block size (samples)
			<24> min frams size (bytes)
			<24> max frams size (bytes)
			<20> Sample rate (Hz)
			<3> (number of channels)-1
			<5> (bits per sample)-1.
			<36> Total samples in stream.
			<128> MD5 signature of the unencoded audio data.
			*/
			context->min_blocksize = (metaDataChunk[0] << 8) | metaDataChunk[1];
			context->max_blocksize = (metaDataChunk[2] << 8) | metaDataChunk[3];
			context->min_framesize = (metaDataChunk[4] << 16) | (metaDataChunk[5] << 8) | metaDataChunk[6];
			context->max_framesize = (metaDataChunk[7] << 16) | (metaDataChunk[8] << 8) | metaDataChunk[9];
			context->samplerate = (metaDataChunk[10] << 12) | (metaDataChunk[11] << 4) | ((metaDataChunk[12] & 0xf0) >> 4);
			context->channels = ((metaDataChunk[12] & 0x0e) >> 1) + 1;
			context->bps = (((metaDataChunk[12] & 0x01) << 4) | ((metaDataChunk[13] & 0xf0) >> 4) ) + 1;
			//This field in FLAC context is limited to 32-bits
			context->totalsamples = (metaDataChunk[14] << 24) | (metaDataChunk[15] << 16) | (metaDataChunk[16] << 8) | metaDataChunk[17];
		} else if((metaDataChunk[0] & 0x7F) == 4) {
			unsigned long fieldLength, commentListLength;
			unsigned long readCount;
			unsigned long totalReadCount = 0;
			unsigned long currentCommentNumber = 0;
			int readAmount;

			f_read(&SDFile, &fieldLength, 4, &s1);
			totalReadCount += s1;
			//Read vendor info
			readCount = 0;
			readAmount = 128;
			while(readCount < fieldLength) {
				if(fieldLength - readCount < readAmount) readAmount = fieldLength - readCount;
				if(readAmount > metaDataBlockLength - totalReadCount) {
					printf("Malformed metadata aborting\r\n");
					f_close(&SDFile);
					return 1;

				}
				f_read(&SDFile, metaDataChunk, readAmount, &s1);
				readCount += s1;
				totalReadCount += s1;
				//terminate the string
				metaDataChunk[s1 - 1] = '\0';
			}

			f_read(&SDFile, &commentListLength, 4, &s1);
			totalReadCount += s1;
			while(currentCommentNumber < commentListLength) {
				f_read(&SDFile, &fieldLength, 4, &s1);
				totalReadCount += s1;
				readCount = 0;
				readAmount = 128;
				while(readCount < fieldLength) {
					if(fieldLength - readCount < readAmount) readAmount = fieldLength - readCount;
					if(readAmount > metaDataBlockLength - totalReadCount) {
						printf("Malformed metadata aborting\r\n");
						f_close(&SDFile);
						return 1;

					}
					f_read(&SDFile, metaDataChunk, readAmount, &s1);
					readCount += s1;
					totalReadCount += s1;
					//terminate the string
					metaDataChunk[s1 - 1] = '\0';

					//Make another with just contents
					tagContents = strchr(metaDataChunk, '=');
					if (!tagContents) {
						continue;
					}
					tagContents[0] = '\0';
					tagContents = &tagContents[1];
					strToUppercase(metaDataChunk);
					if(strcmp(metaDataChunk, "ARTIST") == 0) {
						printf("Artist: %s\r\n", tagContents);
					} else if(strcmp(metaDataChunk, "TITLE") == 0) {
						printf("Title: %s\r\n", tagContents);
					} else if(strcmp(metaDataChunk, "ALBUM") == 0) {
						printf("Album: %s\r\n", tagContents);
					}
				}
				currentCommentNumber++;
			}
			if(f_lseek(&SDFile, SDFile.fptr + metaDataBlockLength - totalReadCount) != FR_OK) {
				printf("File Seek Faile\r\n");
				f_close(&SDFile);
				return 1;
			}
		} else {
			if(f_lseek(&SDFile, SDFile.fptr + metaDataBlockLength) != FR_OK) {
				printf("File Seek Failed\r\n");
				f_close(&SDFile);
				return 1;
			}
		}
	} while(metaDataFlag);

	// track length in ms
	context->length = (context->totalsamples / context->samplerate) * 1000;
	// file size in bytes
	context->filesize = f_size(&SDFile);
	// current offset is end of metadata in bytes
	context->metadatalength = SDFile.fptr;
	// bitrate of file
	context->bitrate = ((context->filesize - context->metadatalength) * 8) / context->length;

	// 无需关闭文件
	// f_close(&FLACfile);
	return 0;
}

// flac 播放
int Flac_Play(char *fname)
{
	int totalsec;

	Is_finished = false;
	Is_End_chunk = false;

	memset(pAudio, 0x00, sizeof(AUDIO_DEV));

	// Get the metadata we need to play the file
	if(Flac_ParceMetadata(fname, &context) != 0) {
		printf("Failed to get FLAC context\r\n");
		return 1;
	}

	totalsec = (int)(context.totalsamples / context.samplerate);

	// 显示flac 信息
	printf("Playing %s\r\n", fname);
	printf("Mode: %s\r\n", context.channels == 1 ? "Mono" : "Stereo");
	printf("Samplerate: %d Hz\r\n", context.samplerate);
	printf("SampleBits: %d bit\r\n", context.bps);
	printf("Samples: %lu\r\n", context.totalsamples);
	printf("min_blocksize: %d\r\n", context.min_blocksize);
	printf("max_blocksize: %d\r\n", context.max_blocksize);
	printf("min_framesize: %d\r\n", context.min_framesize);
	printf("max_framesize: %d\r\n", context.max_framesize);
	printf("total time: %02d:%02d\r\n", totalsec / 60, totalsec % 60);

	pAudio->sampleRate = context.samplerate;
	pAudio->bitsPerSamplePerChannel = context.bps;

	if( (context.max_framesize % 4) != 0) {
		align = 4 - context.max_framesize % 4;
	} else {
		align = 0;
	}

	max_framesize = context.max_framesize + align;
	max_blocksize = context.max_blocksize;

	if( (max_framesize + 18 * max_blocksize) > 101 * 1024) {
		printf("Out of memoey (101 * 1024) !!!\r\n");
		return 13;
	}

	// decodedSamplesLeft =  (int32_t *)decode_Left;
	// decodedSamplesRight = (int32_t *)decode_Right;

	// decodedSamplesLeft = (int32_t *)&buffPointer[0 * max_blocksize];
	// decodedSamplesRight = (int32_t *)&buffPointer[4 * max_blocksize];

	// context.decoded0 = decodedSamplesLeft;
	// context.decoded1 = decodedSamplesRight;

	if( pAudio->bitsPerSamplePerChannel == 16) {

		context.decoded0 = (int32_t *)&buffPointer[0 * max_blocksize];
		context.decoded1 = (int32_t *)&buffPointer[4 * max_blocksize];

		pAudio->buff_A = (uint16_t *)&buffPointer[8 * max_blocksize];
		pAudio->buff_B = (uint16_t *)&buffPointer[12 * max_blocksize];

		fileChunk = (unsigned char *)&buffPointer[16 * max_blocksize];

	} else if( pAudio->bitsPerSamplePerChannel == 24) {

		// context.decoded0 = (int32_t *)decode_Left;
		// context.decoded1 = (int32_t *)decode_Right;

		pAudio->buff_A = (uint16_t *)&buffPointer[0 * max_blocksize];
		pAudio->buff_B = (uint16_t *)&buffPointer[8 * max_blocksize];

		context.decode_tmp = (int32_t *)&buffPointer[16 * max_blocksize];

		fileChunk = (unsigned char *)&buffPointer[18 * max_blocksize];

	}

	// The decoder has sample size defined by FLAC_OUTPUT_DEPTH (currently 29 bit)
	// Shift for lower bitrate to align MSB correctly
	sampleShift = FLAC_OUTPUT_DEPTH - context.bps;

	// Goto start of stream
	if(f_lseek(&SDFile, context.metadatalength) != FR_OK) {
		printf("Seek context.metadatalength: %d Error !!!\r\n", context.metadatalength);
		f_close(&SDFile);
		return 1;
	}

	// Fill up fileChunk completely (MAX_FRAMSIZE = valid size of memory fileChunk points to)
	f_read(&SDFile, fileChunk, max_framesize, &bytesLeft);

	pAudio->play_buff = pAudio->buff_A;
	pAudio->read_buff = pAudio->buff_B;

	printf("\r\nfill buffer_A ...\r\n");
	process_flac_buffer();
	printf("\r\nfill buffer_B ...\r\n");
	process_flac_buffer();

	pAudio->play_size = (uint32_t)(pAudio->bytesRead);
	printf("\r\nPlay_Size = %d\r\n", pAudio->play_size);

	audio_Start();

	return 0;
}




